#include "signway_log.h"
#include <sys/mman.h>
#include <pthread.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <time.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>

#define BUFFER_SIZE 256
#define CLOSE_LOG_FILE_PATH "CLOSE_LOG"
#define MMAP_FILE "MMAP_FILE"

struct mt {
    int num;
    pthread_mutex_t mutex;
    pthread_mutexattr_t mutexattr;
};

static socklen_t len;
static int sockfd = -1;
static char path[128] = { 0 };
static struct sockaddr_un servaddr1, servaddr2;
static char data[1024 + 128] = { 0 };
char __data_log_buffer[1024] = { 0 };
char buf[20] = { 0 };
struct mt *mm;
int mmp_fd = -1;

void signway_log_init(void)
{
    struct sockaddr_un cliaddr;

    sockfd = socket(AF_LOCAL, SOCK_DGRAM, 0);
    len = sizeof(struct sockaddr_un);

    /* local address */
    bzero(&cliaddr, sizeof(cliaddr));
    cliaddr.sun_family = AF_LOCAL;
    snprintf(path, sizeof(path), UNIXDG_PATH "%d.dg", getpid());
    strcpy(cliaddr.sun_path, path);
    bind(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
    // printf("log init %s", path);

    /* remote address */
    bzero(&servaddr1, sizeof(servaddr1));
    servaddr1.sun_family = AF_LOCAL;
    strcpy(servaddr1.sun_path, UNIXDG_PATH2);

    /* remote address */
    bzero(&servaddr2, sizeof(servaddr2));
    servaddr2.sun_family = AF_LOCAL;
    strcpy(servaddr2.sun_path, UNIXDG_PATH3);

	/* mmap */
	mmp_fd = open(MMAP_FILE, O_CREAT | O_RDWR, 0644);
	if (mmp_fd < 0) {
		perror("open");
		return;
	}
	ftruncate(mmp_fd, sizeof(struct mt));
	mm = mmap(NULL, sizeof(*mm), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, mmp_fd, 0);
	if (mm->num != 0x1234) {
		memset(mm, 0, sizeof(*mm));
		mm->num = 0x1234;
		/* init mutex */
		pthread_mutexattr_init(&mm->mutexattr);                                  //初始化mutex属性对象
		pthread_mutexattr_setpshared(&mm->mutexattr, PTHREAD_PROCESS_SHARED);    //修改属性为进程间共享

		pthread_mutex_init(&mm->mutex, &mm->mutexattr);                          //初始化一把mutex琐
	}
}

void signway_log_deinit(void)
{
    if (sockfd > 0) {
        close(sockfd);
        sockfd = -1;
    }
	if (mmp_fd > 0) {
		close(mmp_fd);
		mmp_fd = -1;
	}
}

static void signway_log_writev(signway_log_level_t level,
                   const char *tag,
                   const char *format,
                   va_list args)
{
	pthread_mutex_lock(&mm->mutex);
    int datalen = vsnprintf(data, sizeof(data), format, args);
    printf("%s", data);

    if (sockfd >= 0) {
        sendto(sockfd, data, datalen, 0, (struct sockaddr *)&servaddr1, len);
        sendto(sockfd, data, datalen, 0, (struct sockaddr *)&servaddr2, len);
    }
	pthread_mutex_unlock(&mm->mutex);
}

void signway_log_write(signway_log_level_t level,
                   const char *tag,
                   const char *format, ...)
{

    if ((access(CLOSE_LOG_FILE_PATH, F_OK)) != -1) {
        return;
    }

    va_list list;
    va_start(list, format);
    signway_log_writev(level, tag, format, list);
    va_end(list);
}

/**
 * @return time stamp 时间戳
 */
const char *signway_log_timestamp(void)
{
    uint64_t cur = get_cur_time();
    time_t tim = cur / 1000;
    struct tm *p_tm = NULL;
    p_tm = localtime(&tim);
    snprintf(buf, sizeof(buf), "%02d-%02d %02d:%02d:%02d.%03d", 
        p_tm->tm_mon + 1, p_tm->tm_mday, p_tm->tm_hour, p_tm->tm_min, p_tm->tm_sec, (int)(cur % 1000));
    return buf;
}

#define TIMEVALTOFIXEDPT(tv) ((uint64_t)(tv).tv_sec * 1000 + (tv).tv_usec / 1000)
#define FIXEDPTTOTIMEVAL(fix, tv) {(tv).tv_sec = (fix) / 1000; (tv).tv_usec = ((fix) % 1000) * 1000;}

uint64_t get_cur_time(void)
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return TIMEVALTOFIXEDPT(tv);
}

const char *signway_err_to_name(signway_err_t code)
{
    switch(code) {
    case SIGNWAY_ERR_NO_MEM:              return "Out of memory";
    case SIGNWAY_ERR_INVALID_ARG:         return "Invalid argument";
    case SIGNWAY_ERR_INVALID_STATE:       return "Invalid state";
    case SIGNWAY_ERR_INVALID_SIZE:        return "Invalid size";
    case SIGNWAY_ERR_NOT_FOUND:           return "Requested resource not found";
    case SIGNWAY_ERR_NOT_SUPPORTED:       return "Operation or feature not supported";
    case SIGNWAY_ERR_TIMEOUT:             return "Operation timed out";
    case SIGNWAY_ERR_INVALID_RESPONSE:    return "Received response was invalid";
    case SIGNWAY_ERR_INVALID_CRC:         return "CRC or checksum was invalid";
    case SIGNWAY_ERR_INVALID_VERSION:     return "Version was invalid";
    case SIGNWAY_ERR_INVALID_MAC:         return "MAC address was invalid";
    case SIGNWAY_ERR_NOT_INIT:            return "MAC address was invalid";
    case SIGNWAY_ERR_BUF:                 return "The buffer is too small";

    case SIGNWAY_ERR_MWIFI_BASE:          return "Starting number of MWIFI error codes";
    case SIGNWAY_ERR_MESPNOW_BASE:        return "Starting number of MESPNOW error codes";
    case SIGNWAY_ERR_MCONFIG_BASE:        return "Starting number of MCONFIG error codes";
    case SIGNWAY_ERR_MUPGRADE_BASE:       return "Starting number of MUPGRADE error codes";
    case SIGNWAY_ERR_MDEBUG_BASE:         return "Starting number of MDEBUG error codes";
    case SIGNWAY_ERR_MLINK_BASE:          return "Starting number of MLINK error codes";
    case SIGNWAY_ERR_CUSTOM_BASE:         return "Starting number of COUSTOM error codes";
    default :                             return "nothing find code";
    }
}

void SIGNWAY_ARRAY_LOGI(const char *head, const char *format, void *_array, int len)
{
    int i = 0;
    int pos = 0;
    uint8_t *array = (uint8_t *)_array; 
    if (SIGNWAY_LOG_LEVEL >= SIGNWAY_LOG_DEBUG) {
        sprintf(__data_log_buffer + pos, "%s", head); 
        pos += strlen(head); 
        for (i = 0; i < len; i++) { 
            pos += sprintf(__data_log_buffer + pos, format, array[i]); 
            if ((i + 1) % 255 == 0) { 
                pos += sprintf(__data_log_buffer + pos, "\n"); 
            } 
        } 
        __data_log_buffer[pos] = '\0';
        SIGNWAY_LOGD("%s", __data_log_buffer); 
    }
}
